from lib.conf.config import settings
from lib import loggers
import importlib
import subprocess
import paramiko
import traceback

logger = loggers.get_logger()


class PluginManager:
    def __init__(self, mode=None, hostname=None, port=None, user=None, password=None, key=None):
        self.plugin_dict = settings.PLUGINS_DICT
        self.mode = mode or settings.MODE
        self.hostname = hostname
        self.port = port or settings.SSH_PORT
        self.user = user or settings.SSH_USER
        self.password = password or settings.SSH_PASSWORD
        self.key = key or settings.SSH_KEY
        self.debug = settings.DEBUG

    def exec_plugin(self):
        '''
        获取所有的plugins插件，并执行插件，获取对应的服务器硬件信息
        :return: 字典类型，包含所有PLUGIN_DICT定义的插件功能采集的数据
        '''
        response = {}
        # 获取插件列表，循环调用插件获取该插件功能所采集的数据
        for key, value in self.plugin_dict.items():
            ret = {"status": True, "data": None}

            # 根据value获取对应plugins插件
            path, class_name = value.rsplit('.', maxsplit=1)
            module = importlib.import_module(path)
            cls = getattr(module, class_name)

            # 一个没用的钩子函数initial()
            if hasattr(cls, 'initial'):
                obj = cls().initial()
            else:
                obj = cls()
            try:
                # 执行插件，获取返回结果
                result = obj.process(self.command, self.debug)
                ret['data'] = result
            except Exception as e:
                msg = '[{}] [{}] 采集数据出现错误：{}'.format(
                    self.hostname if self.hostname else "AGENT",
                    class_name,
                    traceback.format_exc()
                )
                logger.error(msg)
                ret['status'] = False
                ret['data'] = msg

            response[key] = ret

        return response

    def command(self, cmd):
        if self.mode == 'SSH':
            return self.__ssh(cmd)
        elif self.mode == 'SALT':
            return self.__salt(cmd)
        elif self.mode == 'AGENT':
            return self.__agent(cmd)
        else:
            logger.error('模式只能是：AGENT/SSH/SALT')

    def __agent(self, cmd):
        output = subprocess.Popen(cmd, shell=True, stdout=subprocess.PIPE, stderr=subprocess.PIPE)
        data = output.stdout.read().decode('utf-8')
        message = cmd + ' 命令执行错误！'
        assert data, message
        return data

    def __salt(self, cmd):
        # cmd = "salt '{}' cmd.run '{}'".format(self.hostname, cmd)
        # output = subprocess.Popen(cmd, shell=True, stdout=subprocess.PIPE, stderr=subprocess.PIPE)
        # data = output.stdout.read().decode('utf-8')
        # message = cmd + ' 命令执行错误！'
        # assert data, message
        # return data
        return self.__ssh(cmd)

    def __ssh(self, cmd):
        # 实例化SSHClient
        client = paramiko.SSHClient()
        # 自动添加策略，保存服务器的主机名和密钥信息，如果不添加，那么不再本地know_hosts文件中记录的主机将无法连接
        client.set_missing_host_key_policy(paramiko.AutoAddPolicy())
        try:
            if self.key == "ssh_key_path":
                # 连接SSH服务器，用户名密码认证
                client.connect(
                    hostname=self.hostname,
                    port=self.port,
                    username=self.user,
                    password=self.password
                )
            else:
                # 连接SSH服务端，以用户名和密钥进行认证
                client.connect(
                    hostname=self.hostname,
                    port=self.port,
                    username=self.user,
                    pkey=self.key
                )
            # 打开一个Chanent并执行命令
            stdin, stdout, stderr = client.exec_command(cmd)
            # stdout 为正确输出，stderr为错误输出，同时只有1个变量有值
            data = stdout.read().decode('utf-8')
            message = cmd + ' 命令执行错误！'
            assert data, message

            return data
        except Exception as e:
            logger.error(e)
        finally:
            client.close()
